#!/usr/bin/env bash

#
# To run this script remotely type this in your shell
# (where <args>... are the arguments you want to pass to JBang):
#   curl -Ls https://sh.jbang.dev | bash -s - <args>...
#

# The Java version to install when it's not installed on the system yet
javaVersion=${JBANG_DEFAULT_JAVA_VERSION:-17}

script_dir() {
  script=${BASH_SOURCE[0]}
  while [ -L "$script" ]; do
    dir=$( cd -P "$( dirname "$script" )" >/dev/null 2>&1 && pwd )
    script=$(readlink "$script")
    [[ $script != /* ]] && script=$dir/$script
  done
  dir=$( cd -P "$( dirname "$script" )" >/dev/null 2>&1 && pwd )
  echo "$dir"
}

download() {
  if [ -x "$(command -v curl)" ]; then
    curl -sLf -o "$2" "$1"
    retval=$?
  elif [ -x "$(command -v wget)" ]; then
    wget -q -O "$2" "$1"
    retval=$?
  else
    echo "Error: curl or wget not found, please make sure one of them is installed" 1>&2
    exit 1
  fi
}

unpack() {
  if [[ "$1" == *.tar.gz ]]; then
    gzip -cd "$1" | tar xf - -C "$2"
    retval=$?
    if [[ $os == mac && $retval -eq 0 ]]; then
      mv "$TDIR/jdks/$javaVersion.tmp/"*/Contents/Home/* "$TDIR/jdks/$javaVersion.tmp/"
      retval=$?
    else
      mv "$TDIR/jdks/$javaVersion.tmp/"*/* "$TDIR/jdks/$javaVersion.tmp/"
    fi
  else
    unzip -qq -o "$1" -d "$2"
    retval=$?
    mv "$TDIR/jdks/$javaVersion.tmp/"*/* "$TDIR/jdks/$javaVersion.tmp/"
  fi
}

javacInPath() {
  [[ -x "$(command -v javac)" ]] && ( [[ $os != "mac" ]] || /usr/libexec/java_home &> /dev/null )
}

# Determines the JDK/JAVA_EXEC to use
setup_java_exec() {
  unset JAVA_EXEC
  if [[ -n "$JAVA_HOME" ]]; then
    # Determine if a (working) JDK is available in JAVA_HOME
    if [ -x "$(command -v "$JAVA_HOME"/bin/javac)" ]; then
      JAVA_EXEC="$JAVA_HOME/bin/java"
    else
      echo "JAVA_HOME is set but does not seem to point to a valid Java JDK" 1>&2
    fi
  fi
  if [[ -z "$JAVA_EXEC" ]]; then
    # Determine if a (working) JDK is available on the PATH
    if javacInPath; then
      unset JAVA_HOME
      JAVA_EXEC="java"
    elif [ -x "$JBDIR/currentjdk/bin/javac" ]; then
      export JAVA_HOME="$JBDIR/currentjdk"
      JAVA_EXEC="$JBDIR/currentjdk/bin/java"
    else
      export JAVA_HOME="$TDIR/jdks/$javaVersion"
      JAVA_EXEC="$JAVA_HOME/bin/java"
      # Check if we installed a JDK before
      if [ ! -d "$TDIR/jdks/$javaVersion" ]; then
        # If not, download and install it
        if [[ $os == "" ]]; then
          echo "Unable to download JDK, unsupported Operating System: $(uname -s)" 1>&2
          exit 1
        fi
        if [[ $arch == "" ]]; then
          echo "Unable to download JDK, unsupported Architecture: $(uname -m)" 1>&2
          exit 1
        fi
        mkdir -p "$TDIR/jdks"
        echo "Downloading JDK $javaVersion. Be patient, this can take several minutes..." 1>&2
        jdkurl="https://api.foojay.io/disco/v3.0/directuris?distro=$distro&javafx_bundled=false&libc_type=$libc_type&archive_type=$file_type&operating_system=$os&package_type=jdk&version=$javaVersion&architecture=$arch&latest=available"
        download "$jdkurl" "$TDIR/bootstrap-jdk.$file_type"
        if [ $retval -ne 0 ]; then echo "Error downloading JDK" 1>&2; exit $retval; fi
        echo "Installing JDK $javaVersion..." 1>&2
        rm -rf "$TDIR/jdks/$javaVersion.tmp/"
        mkdir -p "$TDIR/jdks/$javaVersion.tmp"
        unpack "$TDIR/bootstrap-jdk.$file_type" "$TDIR/jdks/$javaVersion.tmp"
        if [ $retval -ne 0 ]; then
          # Check if the JDK was installed properly
          javac -version > /dev/null 2>&1
          retval=$?
        fi
        if [ $retval -ne 0 ]; then echo "Error installing JDK" 1>&2; exit $retval; fi
        # Activate the downloaded JDK giving it its proper name
        mv "$TDIR/jdks/$javaVersion.tmp" "$TDIR/jdks/$javaVersion"
        # Set the current JDK
        "${JAVA_EXEC}" -classpath "${jarPath}" dev.jbang.Main jdk default "$javaVersion"
      fi
    fi
  fi
}

# Executes jbang (either native binary or JAR) and handles the output
execute_jbang() {
## https://stackoverflow.com/questions/1668649/how-to-keep-quotes-in-bash-arguments
## attempt to ensure each argument keeps its original quoting

  export JBANG_RUNTIME_SHELL=bash
  JBANG_STDIN_NOTTY=$([ -t 0 ] && echo "false" || echo "true")
  export JBANG_STDIN_NOTTY
  export JBANG_LAUNCH_CMD=$0
  
  ## run it using command substitution to have just the user process once jbang is done
  if [[ -n "$binaryPath" ]]; then
    # Run native binary
    output=$(CLICOLOR_FORCE=1 "$binaryPath" "$@")
    err=$?
  else
    # Run JAR
    output=$(CLICOLOR_FORCE=1 "${JAVA_EXEC}" ${JBANG_JAVA_OPTIONS} -classpath "${jarPath}" dev.jbang.Main "$@")
    err=$?
  fi
  
  if [ $err -eq 255 ]; then
    eval "exec $output"
  elif [ -n "$output" ]; then
    echo "$output"
    exit $err
  else
    exit $err
  fi
}

abs_jbang_dir=$(script_dir)

# todo might vary by os so can be overwritten below
libc_type=glibc
file_type=tar.gz

case "$(uname -s)" in
  Linux*)
    os=linux
    if [ -f /etc/alpine-release ]; then
      os=alpine-linux
      javaVersion=16
    fi
    ;;
  Darwin*)
    os=mac
    libc_type=libc;;
  CYGWIN*|MINGW*|MSYS*)
    os=windows
    libc_type=c_std_lib
    file_type=zip;;
  AIX)
    os=aix;;
  *)
    os=
esac

case "$(uname -m)" in
  i?86)
    arch=x32;;
  x86_64|amd64)
    arch=x64;;
  aarch64)
    arch=aarch64;;
  armv7l)
    arch=arm;;
  ppc64le)
    arch=ppc64le;;
  s390x)
    arch=s390x;;
  arm64)
    arch=arm64;;
  riscv64)
    arch=riscv64
    ;;
  *)
    ## AIX gives a machine ID for `uname -m` but it only supports ppc64
    if [ "$os" = "aix" ]; then
      arch=ppc64
    else
      arch=
    fi
    ;;
esac

## when using cygwin fall out to just running the bat file.
if [[ $os == windows && -f "$abs_jbang_dir/jbang.cmd" && "$(uname -s)" == CYGWIN* ]]; then
  cmd /c "$(cygpath -m "$abs_jbang_dir"/jbang.cmd)" "$@"
  exit $?
fi

if [[ -z "$JBANG_JDK_VENDOR" ]]; then
  if [[ "$javaVersion" -eq 8 || "$javaVersion" -eq 11 || "$javaVersion" -ge 17 ]]; then
    distro="temurin";
  else
    distro="aoj"; fi
else
  distro=$JBANG_JDK_VENDOR
fi

if [[ -z "$JBANG_DIR" ]]; then JBDIR="$HOME/.jbang"; else JBDIR="$JBANG_DIR"; fi
if [[ -z "$JBANG_CACHE_DIR" ]]; then TDIR="$JBDIR/cache"; else TDIR="$JBANG_CACHE_DIR"; fi
if [[ -z "$JBANG_USE_NATIVE" ]]; then JBANG_USE_NATIVE="false"; fi

## resolve jbang.bin binary or jar path from script location
binaryPath=""
jarPath=""
if [[ "$JBANG_USE_NATIVE" == "true" ]]; then
  # Look for native binary first if enabled
  if [[ "$os" == "windows" ]]; then
    if [ -f "$abs_jbang_dir/jbang.bin.exe" ] && [ -x "$abs_jbang_dir/jbang.bin.exe" ]; then
      binaryPath="$abs_jbang_dir/jbang.bin.exe"
    else
      echo "WARNING: JBang native binary (jbang.bin.exe) not found in $abs_jbang_dir" 1>&2
    fi
  else
    if [ -f "$abs_jbang_dir/jbang.bin" ] && [ -x "$abs_jbang_dir/jbang.bin" ]; then
      binaryPath="$abs_jbang_dir/jbang.bin"
    else
      echo "WARNING: JBang native binary (jbang.bin) not found or not executable in $abs_jbang_dir" 1>&2
    fi
  fi
fi
if [[ -z "$binaryPath" ]]; then
  # Fall back to JAR if no native binary found or native binary disabled
  if [ -f "$abs_jbang_dir/jbang.jar" ]; then
    jarPath=$abs_jbang_dir/jbang.jar
  elif [ -f "$abs_jbang_dir/.jbang/jbang.jar" ]; then
    jarPath=$abs_jbang_dir/.jbang/jbang.jar
  fi
fi
if [[ -z "$binaryPath" && -z "$jarPath" ]]; then
  if [[ ! -f "$JBDIR/bin/jbang.jar" || ! -f "$JBDIR/bin/jbang" ]]; then
    echo "Downloading JBang $JBANG_DOWNLOAD_VERSION..." 1>&2
    mkdir -p "$TDIR/urls"
    if [ -z "$JBANG_DOWNLOAD_VERSION" ]; then
      jburl="https://github.com/jbangdev/jbang/releases/latest/download/jbang.tar";
    else
      jburl="https://github.com/jbangdev/jbang/releases/download/v$JBANG_DOWNLOAD_VERSION/jbang.tar";
    fi
    download "$jburl" "$TDIR/urls/jbang.tar"
    if [ $retval -ne 0 ]; then echo "Error downloading JBang from $jburl" 1>&2; exit $retval; fi
    echo "Installing JBang..." 1>&2
    rm -rf "$TDIR/urls/jbang"
    tar xf "$TDIR/urls/jbang.tar" -C "$TDIR/urls"
    if [ $retval -ne 0 ]; then echo "Error installing JBang" 1>&2; exit $retval; fi
    mkdir -p "$JBDIR/bin"
    rm -f "$JBDIR/bin/jbang" "$JBDIR/bin"/jbang.*
    cp -f "$TDIR/urls/jbang/bin"/* "$JBDIR/bin"
  fi
  "$JBDIR"/bin/jbang "$@"
  exit $?
fi

if [ -f "$jarPath.new" ]; then
  # a new jbang version was found, we replace the old one with it
  mv "$jarPath.new" "$jarPath"
fi

# Only setup Java if we're running the JAR version
if [[ -z "$binaryPath" ]]; then
  setup_java_exec
fi

execute_jbang "$@"
